using System;
using System.Collections.Generic;
using CGF;
#if UNITY_WEBGL
using System.Runtime.InteropServices;
using AOT;
using UnityEngine;
#endif

// 不同与 IndexedDB 类，该类进自持key-value，用于存储 list 文件和 AssetBundle
public class IndexedDBHelper
{
    // 回调委托定义
    // dbNamePtr 在本工具类中不使用，因为和 IndexedDB 类公用jslib文件，只能补齐参数
    private delegate void Callback(IntPtr dbNamePtr);
    private delegate void StringCallback(IntPtr dbNamePtr, IntPtr stringPtr);
    private delegate void BinaryCallback(IntPtr dbNamePtr, IntPtr dataPtr, int size);

#if UNITY_WEBGL
    // JavaScript 插件函数声明
    [DllImport("__Internal")]
    private static extern void IndexedDB_Init(string db, int version, string storesJson, Callback finishCallback);
    [DllImport("__Internal")]
    private static extern void IndexedDB_SetKeyValue(string db, string key, string stringValue, byte[] binaryValue, int size);
    [DllImport("__Internal")]
    private static extern void IndexedDB_GetKeyValue(string db, string key, StringCallback stringCallback, BinaryCallback binaryCallback);
    [DllImport("__Internal")]
    private static extern void IndexedDB_DeleteKey(string db, string key);
    [DllImport("__Internal")]
    private static extern void IndexedDB_SyncFS();
#endif

    private class Request
    {
        public string key;
        public Action<string> stringCallback;
        public Action<byte[]> bytesCallback;
    }
    private static bool isLoadKvBusy;
    private static Queue<Request> loadKvQueue = new Queue<Request>();

    // 实例回调存储
    private static Action<string, byte[]> _currentKvCallback;

    // 初始化状态
    private static string dbName = "list_and_ab";
    private static int dbVersion = 1; //改结构要手动+1
    private static bool _isInitialized;
    private static bool _isInitializing;
    private static CTask _initTask;

    // 初始化数据库
    public static async CTask Init()
    {
#if UNITY_WEBGL
        if (_isInitialized || _isInitializing || Application.isEditor) return;
        _isInitializing = true;
        await CTask.Wait(0.5f);
        _initTask = CTask.Create();
        try
        {
            IndexedDB_Init(dbName, dbVersion, null, InitCallback);
        }
        catch (Exception e)
        {
            Debug.LogError("Failed to initialize IndexedDB: " + e.Message);
            _isInitializing = false;
        }
        await _initTask;
#endif
    }

    public static void SetKeyValue(string key, string value)
    {
#if UNITY_WEBGL
        if (!_isInitialized || Application.isEditor)
        {
            Debug.LogError("IndexedDBHelper 未初始化");
            return;
        }
        try
        {
            IndexedDB_SetKeyValue(dbName, key, value, null, 0);
            IndexedDB_SyncFS();
        }
        catch (Exception e)
        {
            Debug.LogError("Failed to SetKeyValue: " + e.Message);
        }
#endif
    }

    public static void SetKeyValue(string key, byte[] value)
    {
#if UNITY_WEBGL
        if (!_isInitialized || Application.isEditor)
        {
            Debug.LogError("IndexedDBHelper 未初始化");
            return;
        }
        try
        {
            IndexedDB_SetKeyValue(dbName, key, null, value, value.Length);
            IndexedDB_SyncFS();
        }
        catch (Exception e)
        {
            Debug.LogError("Failed to SetKeyValue: " + e.Message);
        }
#endif
    }

    public static void GetValueByKey(string key, Action<string> callback)
    {
#if UNITY_WEBGL
        if (!_isInitialized || Application.isEditor)
        {
            Debug.LogError("IndexedDBHelper 未初始化");
            return;
        }
        if (isLoadKvBusy)
        {
            loadKvQueue.Enqueue(new Request()
            {
                key = key,
                stringCallback = callback,
            });
            return;
        }
        try
        {
            _currentKvCallback = (str, bytes) =>
            {
                callback?.Invoke(str);
            };
            isLoadKvBusy = true;
            IndexedDB_GetKeyValue(dbName, key, OnStringLoaded, null);
        }
        catch (Exception e)
        {
            Debug.LogError("Failed to GetValueByKey: " + e.Message);
        }
#endif
    }

    public static void GetValueByKey(string key, Action<byte[]> callback)
    {
#if UNITY_WEBGL
        if (!_isInitialized || Application.isEditor)
        {
            Debug.LogError("IndexedDBHelper 未初始化");
            return;
        }
        if (isLoadKvBusy)
        {
            loadKvQueue.Enqueue(new Request()
            {
                key = key,
                bytesCallback = callback,
            });
            return;
        }
        try
        {
            _currentKvCallback = (str, bytes) =>
            {
                callback?.Invoke(bytes);
            };
            isLoadKvBusy = true;
            IndexedDB_GetKeyValue(dbName, key, null, OnBinaryLoaded);
        }
        catch (Exception e)
        {
            Debug.LogError("Failed to GetValueByKey: " + e.Message);
        }
#endif
    }
    
    public void DeleteKey(string key)
    {
#if UNITY_WEBGL
        if (!_isInitialized || Application.isEditor)
        {
            Debug.LogError("IndexedDBHelper 未初始化");
            return;
        }
        try
        {
            IndexedDB_DeleteKey(dbName, key);
            IndexedDB_SyncFS();
        }
        catch (Exception e)
        {
            Debug.LogError("Failed to DeleteKey: " + e.Message);
        }
#endif
    }

    #if UNITY_WEBGL
    [MonoPInvokeCallback(typeof(Callback))]
    private static void InitCallback(IntPtr dbNamePtr)
    {
        _isInitialized = true;
        _isInitializing = false;
        Debug.Log("IndexedDB initialized successfully.");
        _initTask.SetResult();
    }
    
    [MonoPInvokeCallback(typeof(BinaryCallback))]
    private static void OnBinaryLoaded(IntPtr dbNamePtr, IntPtr dataPtr, int size)
    {
        byte[] result = null;
        if (dataPtr != IntPtr.Zero && size > 0)
        {
            try
            {
                result = new byte[size];
                Marshal.Copy(dataPtr, result, 0, size);
                // 释放内存
                Marshal.FreeHGlobal(dataPtr);
            }
            catch (Exception e)
            {
                Debug.LogError("Error copying binary data: " + e.Message);
            }
        }
        _currentKvCallback(null, result);
        if (loadKvQueue.Count > 0)
        {
            Request next = loadKvQueue.Dequeue();
            bool isBinary = next.bytesCallback != null;
            _currentKvCallback = (str, bytes) =>
            {
                if (isBinary)
                {
                    next.bytesCallback(bytes);
                }
                else
                {
                    next.stringCallback(str);
                }
            };
            if (isBinary)
            {
                IndexedDB_GetKeyValue(dbName, next.key, null, OnBinaryLoaded);
            }
            else
            {
                IndexedDB_GetKeyValue(dbName, next.key, OnStringLoaded, null);
            }
        }
        else
        {
            _currentKvCallback = null;
            isLoadKvBusy = false;
        }
    }

    [MonoPInvokeCallback(typeof(StringCallback))]
    private static void OnStringLoaded(IntPtr dbNamePtr, IntPtr stringPtr)
    {
        string result = ParseString(stringPtr);
        _currentKvCallback(result, null);
        if (loadKvQueue.Count > 0)
        {
            Request next = loadKvQueue.Dequeue();
            bool isBinary = next.bytesCallback != null;
            _currentKvCallback = (str, bytes) =>
            {
                if (isBinary)
                {
                    next.bytesCallback(bytes);
                }
                else
                {
                    next.stringCallback(str);
                }
            };
            if (isBinary)
            {
                IndexedDB_GetKeyValue(dbName, next.key, null, OnBinaryLoaded);
            }
            else
            {
                IndexedDB_GetKeyValue(dbName, next.key, OnStringLoaded, null);
            }
        }
        else
        {
            _currentKvCallback = null;
            isLoadKvBusy = false;
        }
    }

    private static string ParseString(IntPtr ptr)
    {
        try
        {
            if (ptr == IntPtr.Zero)
            {
                Debug.LogWarning("Attempted to parse null pointer");
                return string.Empty;
            }
            string result = Marshal.PtrToStringAnsi(ptr);
            // 如果字符串是通过Marshal.AllocHGlobal分配的，需要释放内存
            Marshal.FreeHGlobal(ptr);
            return result ?? string.Empty;
        }
        catch (Exception e)
        {
            Debug.LogError("Error parsing string: " + e.Message);
            return string.Empty;
        }
    }
#endif
}
